package event

import (
	"net"
	"sync"
	"time"
)

type Client struct {
	Conn         *net.TCPConn
	Host         string
	IsRunning    bool
	Port         int
	ConnectHost  string
	ConnectPort  int
	PingInterval int
	ClientId     [4]byte
	Mutex        sync.Mutex
	EventBus     *EventBus
}

func NewClient() *Client {
	eb := new(EventBus)
	eb.Bus = make(map[string][]EventHandler)
	return &Client{
		IsRunning:    false,
		Port:         0,
		ConnectHost:  "127.0.0.1",
		ConnectPort:  DEFAULT_SERVER_PORT,
		PingInterval: 5,
		ClientId:     [4]byte{0, 0, 0, 0},
		Mutex:        sync.Mutex{},
		EventBus:     eb,
	}
}

func (c *Client) Start() {
	addr := &net.TCPAddr{IP: net.ParseIP(c.ConnectHost), Port: c.ConnectPort}
	conn, err := net.DialTCP("tcp", nil, addr)
	if err != nil {
		panic(err)
	}
	c.Conn = conn
	go c.listen()
	go c.pings()
	c.Reg()
}

func (c *Client) reConnect() {
	addr := &net.TCPAddr{IP: net.ParseIP(c.ConnectHost), Port: c.ConnectPort}
	conn, err := net.DialTCP("tcp", nil, addr)
	if err != nil {
		// panic(err)
		time.Sleep(time.Second)
		c.reConnect()
	}
	c.Conn = conn
}

func (c *Client) Send(data []byte) (int, error) {
	c.Mutex.Lock()
	defer c.Mutex.Unlock()
	n, err := c.Conn.Write(data)
	if err != nil {
		c.reConnect()
		println("send Error", err.Error())
	}
	return n, err
}

func (c *Client) Write(data []byte) (int, error) {
	return c.Send(data)
}

func (c *Client) Close() {
	c.Conn.Close()
}

func (c *Client) listen() {
	for {
		data := make([]byte, 1024)
		n, err := c.Conn.Read(data)
		if err != nil {
			//链接断开，触发重连机制
			// panic(err)
			c.reConnect()
			continue
		}
		c.handle(data[:n])
	}
}

func (c *Client) handle(data []byte) {
	if !isValidMessage(data) {
		return
	}
	switch data[8] {
	case MessageTypeRegister:
		c.ClientId = NewMessageFromMessage(data).ClientId
	case MessageTypePong:
		println("client got Pong")
	case MessageTypeEvent:
		c.HandleEventMessage(data)
	}
}

func (c *Client) Reg() {
	message := NewRegMessage(c.ClientId)
	c.Send(message[:])
}
func (c *Client) UnReg() {
	message := NewUnRegMessage(c.ClientId)
	c.Send(message[:])
}
func (c *Client) Ping() {
	message := NewPingMessage(c.ClientId)
	c.Send(message[:])
}

func (c *Client) pings() {
	for {
		time.Sleep(time.Second * time.Duration(c.PingInterval))
		c.Ping()
	}
}

func (c *Client) Subscribe(topic string, hd EventHandler) {
	c.EventBus.Subscribe(topic, hd)
	message := NewEventRegMessage(c.ClientId, topic)
	c.Send(message)
}

func (c *Client) UnSubscribe(topic string) {
	//todo
}

func (c *Client) HandleEventMessage(data []byte) {
	eventMessage := new(EventMessage)
	eventMessage.FromBytes(data[32:])
	c.EventBus.Publish(eventMessage.Topic, eventMessage.Data)
}

func (c *Client) Publish(name string, data interface{}) {
	eventMessagee := new(EventMessage)
	eventMessagee.Topic = name
	eventMessagee.Data = data
	message := NewEventMessage(c.ClientId, eventMessagee.ToBytes())
	c.Send(message)
}
